 
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Snowy.Extensions;
using Snowy.Properties;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Text;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.Json;

namespace Snowy.Services
{
    public class ClickWordCaptcha 
    {
        private readonly IMemoryCache _memoryCache;
        private readonly IWebHostEnvironment _env;
        private readonly ThirdPartyFonts _fonts;
        private readonly Image[] _images;
        private readonly AppSettings _settings;
        private readonly ILogger<ClickWordCaptcha> _logger;

        public ClickWordCaptcha(IMemoryCache memoryCache, IWebHostEnvironment env, ThirdPartyFonts fonts, IOptions<AppSettings> options, ILogger<ClickWordCaptcha> logger)
        {
            _memoryCache = memoryCache;
            _env = env;
            _fonts = fonts;
            _images = new Image[]{ Resources._1, Resources._2, Resources._3, Resources._4, Resources._5 };
            _settings = options.Value;
            _logger = logger;
        }

       
        public ClickWordCaptchaResult CreateCaptchaImage(string code, int width, int height)
        {
            var rtnResult = new ClickWordCaptchaResult();
            int rightCodeLength = 3;
            Bitmap bitmap = null;
            Graphics g = null;
            MemoryStream ms = null;
            Random random = new Random();
            Color[] colorArray = { Color.Black, Color.DarkBlue, Color.Green, Color.Orange, Color.Brown, Color.DarkCyan, Color.Purple };
            bitmap = new Bitmap(_images[new Random(DateTime.Now.Millisecond).Next(_images.Length)], width, height);
            g = Graphics.FromImage(bitmap);
            Color[] penColor = { Color.Red, Color.Green, Color.Blue };
            int code_length = code.Length;
            List<string> words = new List<string>();
            for (int i = 0; i < code_length; i++)
            {
                int colorIndex = random.Next(colorArray.Length);
                Font f = _fonts.RandThirdPartyFonts(15, FontStyle.Regular);
                Brush b = new SolidBrush(colorArray[colorIndex]);
                int _y = random.Next(height);
                if (_y > (height - 30))
                    _y = _y - 60;

                int _x = width / (i + 1);
                if ((width - _x) < 50)
                {
                    _x = width - 60;
                }
                string word = code.Substring(i, 1);
                if (rtnResult.repData.point.Count < rightCodeLength)
                {
                    if (random.Next(0, 3).Equals(1) || (code_length - i).Equals(rightCodeLength - rtnResult.repData.point.Count))
                    {
                        rtnResult.repData.point.Add(new PointPosModel()
                        {
                            X = _x, 
                            Y = _y 
                        });
                        words.Add(word);
                    }
                }
                g.DrawString(word, f, b, _x, _y);
            }
            rtnResult.repData.wordList = words;

            ms = new MemoryStream();
            bitmap.Save(ms, ImageFormat.Jpeg);
            g.Dispose();
            bitmap.Dispose();
            ms.Dispose();
            rtnResult.repData.originalImageBase64 = Convert.ToBase64String(ms.GetBuffer()); //"data:image/jpg;base64," +
            rtnResult.repData.token = new Random(DateTime.Now.Second).Next().ToString();

            // 缓存验证码正确位置集合
            var cacheOptions = new MemoryCacheEntryOptions().SetSlidingExpiration(TimeSpan.FromSeconds(30));
            _memoryCache.Set(_settings.JwtIssuer+ rtnResult.repData.token, rtnResult.repData.point, cacheOptions);

            rtnResult.repData.point = null; // 清空位置信息
            return rtnResult;
        }
         
        public string RandomCode(int number)
        {
            var sb = new StringBuilder();
            for (var i = 0; i < number; i++)
            {
                sb.Append((char)(new Random().Next(0x4e00, 0x9fa5 + 1)));
            }
            return sb.ToString();
        }
      
   
        public dynamic CheckCode(ClickWordCaptchaInput input)
        {
            var res = new ClickWordCaptchaResult();

            var rightVCodePos = _memoryCache.Get(_settings.JwtIssuer + input.Token) as List<PointPosModel>;
            if (rightVCodePos == null)
            {
                res.repCode = "6110";
                res.repMsg = "验证码已失效，请重新获取";
                return res;
            }

            var userVCodePos = JsonSerializer  .Deserialize<List<PointPosModel>>(input.PointJson);
            if (userVCodePos == null || userVCodePos.Count < rightVCodePos.Count)
            {
                res.repCode = "6111";
                res.repMsg = "验证码无效";
                return res;
            }

            int allowOffset = 25; // 允许的偏移量(点触容错)
            for (int i = 0; i < userVCodePos.Count; i++)
            {
                var xOffset = userVCodePos[i].X - rightVCodePos[i].X;
                var yOffset = userVCodePos[i].Y - rightVCodePos[i].Y;
                xOffset = Math.Abs(xOffset); // x轴偏移量                
                yOffset = Math.Abs(yOffset); // y轴偏移量
                // 只要有一个点的任意一个轴偏移量大于allowOffset，则验证不通过
                if (xOffset > allowOffset || yOffset > allowOffset)
                {
                    res.repCode = "6112";
                    res.repMsg = "验证码错误";
                    return res;
                }
            }

            _memoryCache.Remove(_settings.JwtIssuer + input.Token);
            res.repCode = "0000";
            res.repMsg = "验证成功";
            return res;
        }
    }

    /// <summary>
    /// 记录正确位置
    /// </summary>
    public class PointPosModel
    {
        public int X { get; set; }

        public int Y { get; set; }
    }
    public class ClickWordCaptchaInput
    {
     
        public string CaptchaType { get; set; }

   
        public string PointJson { get; set; }

     
        public string Token { get; set; }
    }
  
    public class ClickWordCaptchaResult
    {
        public string repCode { get; set; } = "0000";
        public string repMsg { get; set; }
        public RepData repData { get; set; } = new RepData();
        public bool error { get; set; }
        public bool success { get; set; } = true;
    }

    public class RepData
    {
        public string captchaId { get; set; }
        public string projectCode { get; set; }
        public string captchaType { get; set; }
        public string captchaOriginalPath { get; set; }
        public string captchaFontType { get; set; }
        public string captchaFontSize { get; set; }
        public string secretKey { get; set; }
        public string originalImageBase64 { get; set; }
        public List<PointPosModel> point { get; set; } = new List<PointPosModel>();
        public string jigsawImageBase64 { get; set; }
        public List<string> wordList { get; set; } = new List<string>();
        public string pointList { get; set; }
        public string pointJson { get; set; }
        public string token { get; set; }
        public bool result { get; set; }
        public string captchaVerification { get; set; }
    }

    public class WordList
    {
    }
}